package com.ybear.ybcomponent.widget.linkage.effector

import android.animation.ValueAnimator
import android.view.MotionEvent
import com.ybear.ybcomponent.widget.linkage.effector.base.CoreEffector
import com.ybear.ybcomponent.widget.linkage.enums.ViewLinkage
import kotlin.math.abs

/**
 * ExtendEffector 实现视图延伸效果器，用于在用户滑动时实现视图高度的延伸动画效果。
 *
 * 核心思路：
 * 1. 在 ACTION_DOWN 时记录原始视图高度及起始触摸位置。
 * 2. 在 ACTION_MOVE 时根据滑动距离动态改变视图高度，制造延伸效果。
 * 3. 在 ACTION_UP 或 ACTION_CANCEL 时，通过动画将视图恢复到目标高度（延伸到终点或恢复原位）。
 */
open class ExtendEffector : CoreEffector() {


    // 原始高度，初始时使用 Int.MIN_VALUE 表示未赋值
    open var originalHeight = Int.MIN_VALUE
        protected set
    // 当前高度（记录 ACTION_DOWN 时的高度）
    open var currentHeight = 0
        protected set

    // 动画时长，默认 500 毫秒，可通过 durationRatio 调整实际时长
    open var duration = 500
    // 动画时长比例
    open var durationRatio = 1F
    // 滑动距离与高度变化的比例系数
    open var distanceRatio = 1F

    // 指定该效果器对应的联动类型为延伸效果
    override fun onViewLinkage(): ViewLinkage = ViewLinkage.EXTEND

    /**
     * onEffector 处理触摸事件，主要根据滑动距离调整 childView 的高度，
     * 实现下拉延伸效果，并在手指离开时回复到合适的高度。
     *
     * @param startOffset 滑动开始生效的最小偏移量
     * @param endOffset   滑动达到的最大偏移量
     * @return 是否处理了当前事件（true 表示处理，否则 false）
     */
    override fun onEffector(startOffset: Float, endOffset: Float): Boolean {
        // 如果终点偏移为 0 或动画正在运行时，不处理事件
        if ( isAnimRunning ) return false

        // 根据触摸事件的不同类型处理不同逻辑
        when ( motionEvent.action ) {
            MotionEvent.ACTION_DOWN -> {
                // ACTION_DOWN：记录原始高度和初始触摸点
                var height = childView.height
                height = if( height < 0 ) {
                    childView.measure( 0, 0 )
                    childView.measuredHeight
                }else {
                    childView.height
                }
                if ( originalHeight == Int.MIN_VALUE ) originalHeight = height
                currentHeight = height
            }
            MotionEvent.ACTION_MOVE -> {
                if( originalHeight == Int.MIN_VALUE ) return false
                // ACTION_MOVE：计算滑动距离并动态调整视图高度
                val evY = motionEvent.y
                // 如果滑动距离未达到生效范围或超过终点偏移，则不处理
                if ( endOffset != 0F && abs( evY.dp2Px ) > endOffset ) return false

                // 根据滑动距离计算新的高度：
                // 1. 计算真实距离（从当前高度出发减去滑动距离，再扣除开始偏移）
                val reallyDistance = currentHeight - evY - startOffset
                // 2. 根据 distanceRatio 缩放距离后转换为整型高度
                val distanceHeight = ( reallyDistance * distanceRatio ).toInt()
                val height = distanceHeight.coerceAtLeast( originalHeight )
                // 3. 确保高度不低于原始高度
                updateHeight( height )
                // 返回 true 表示该事件在指定的距离范围内被处理
                return abs( evY.dp2Px ) in startOffset..endOffset
            }
            MotionEvent.ACTION_UP,
            MotionEvent.ACTION_CANCEL -> {
                /* ACTION_UP / ACTION_CANCEL：手指离开屏幕时，根据当前高度决定回复的目标高度 */
                // 如果当前触摸点高于按下时的点，说明是向下滑动，不进行回复动画
                if (  originalHeight == Int.MIN_VALUE || motionEvent.y >= downScrollY ) return false
                // 延展动画
                val viewHeight = childView.height
                // 判断是否超过了偏移量的一半
                // 如果超出，则延伸到终点偏移；否则恢复到初始高度
                val isExtend = viewHeight - currentHeight >= ( endOffset - currentHeight ) / 2
                if( !isExtend ) return false
                // 创建并启动延展动画，将高度从当前高度变化到目标高度
                handlerCreateExtendAnimator().start()
            }
        }
        // 默认返回 false，表示该事件没有被完全消费
        return false
    }

    /**
     * 恢复原始效果
     * @param animator  是否采用动画
     */
    override fun restoreEffector(animator: Boolean) {
        if( !animator ) {
            updateHeight( originalHeight )
            return
        }
        createNumberAnimator(
            // 起始
            currentHeight,
            // 终止
            originalHeight,
            // 回弹时长
            duration()
        ) {
                newHeight -> updateHeight( newHeight )
        }.start()
    }

    open fun handlerCreateExtendAnimator() : ValueAnimator {
        return createNumberAnimator(
            // 起始
            viewHeight(),
            // 终止
            endOffset.toInt(),
            // 回弹时长
            duration()
        ) {
                newHeight -> updateHeight( newHeight )
        }
    }

    open fun duration() : Long = ( duration * durationRatio ).toLong()

    /**
     * 更新高度
     */
    open fun updateHeight(newHeight: Int) {
        childView.layoutParams = childView.layoutParams.apply { height = newHeight }
        childView.requestLayout()
    }
}